#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

#include "loragw_gpio.h"


#define MSG(args...)  
//printf(args)


#define MAX_QUEUE_EVENT_NUM 10

#define ESP_INTR_FLAG_DEFAULT 0

static bool isGpioIntInstalled = false;

static xQueueHandle gpio_evt_queue = NULL;

/*!
 * Hardware IO IRQ callback function definition
 */
typedef void ( GpioIrqHandler )( void );

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    // gpio_interrupt_t* gpio_interrupt = (gpio_interrupt_t*)arg;    
    xQueueSendFromISR(gpio_evt_queue, &arg, NULL);
}

static void thread_gpio_task(void* arg)
{
    gpio_interrupt_t* gpio_interrupt;
    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &gpio_interrupt, portMAX_DELAY)) 
        {
             gpio_interrupt->callback();
        }
    }
}

void gpio_set_interrupt(gpio_interrupt_t* gpio)
{
    gpio_config_t io_conf;
    //interrupt of rising edge
    io_conf.intr_type = gpio->intType;
    //bit mask of the pins, use gpio->gpio_num here
    io_conf.pin_bit_mask = (1ULL<<gpio->gpio_num);
    //set as input mode    
    io_conf.mode = GPIO_MODE_INPUT;

    switch(gpio->pullMode)
    {
        case GPIO_PULLUP_ONLY:
            //enable pull-up mode
            io_conf.pull_up_en = 1;
            io_conf.pull_down_en = 0;
        break;

        case GPIO_PULLDOWN_ONLY:
            //enable pull-down mode
            io_conf.pull_up_en = 0;
            io_conf.pull_down_en = 1;
        break;

        case GPIO_PULLUP_PULLDOWN:
            //enable pull-down&pull-up mode
            io_conf.pull_up_en = 1;
            io_conf.pull_down_en = 1;
        break;

        default:
        break;
        
    }

    gpio_config(&io_conf);

    if(isGpioIntInstalled == false)
    {
        //install gpio isr service
        gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    
        //create a queue to handle gpio event from isr
        gpio_evt_queue = xQueueCreate(MAX_QUEUE_EVENT_NUM, sizeof(gpio_interrupt_t*));
        //start gpio task
        xTaskCreate(thread_gpio_task, "gpio_task", 4096, NULL, 10, NULL);

        isGpioIntInstalled = true;
    }
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(gpio->gpio_num, gpio_isr_handler, (void*) gpio);
}
